package timingAttack;

import java.util.Arrays;

public class Statistics 
{

	/***
	 * Calculate the covariance of two long arrays
	 * Use the formula Cov(x,y) = Sum( [X-Xbar][Y-Ybar] ) / N
	 * 
	 * @param x
	 * @param y
	 * @return cov(x,y)
	 * @throws Exception if the array lengths differ
	 */
	static double covariance(long [] x, long [] y) throws Exception
	{
		if( x.length != y.length )
			throw new Exception("Array lengths must match");
		
		double xmean = mean(x);
		double ymean = mean(y);
		double total = 0;
		
		for( int i = 0; i < x.length; i++ )
			total += (x[i]-xmean)*(y[i]-ymean);

		return total / x.length;
	}

	/***
	 * Calculate the Pearson correlation coefficient for two long arrays
	 * Use the formula Cor(x,y) = Cov(x,y)/std(x)/std(y)
	 * 
	 * @param x
	 * @param y
	 * @return cor(x,y)
	 * @throws Exception 
	 */
	static double correlation(long [] x, long [] y) throws Exception
	{
		return covariance(x,y) / Math.sqrt(variance(x)) / Math.sqrt(variance(y));
	}
	
	/***
	 * Calculate the variance of an array of doubles
	 * Use the formula Var = E[X^2] - E[X]^2
	 * 
	 * @param data
	 * @return
	 */
	static double variance(double [] data)
	{
		double result = 0;
		double xbar = mean(data);
		
		for( int i = 0; i < data.length; i++ )
		{
			result += ( (double) data[i]*data[i] );
		}
		
		result /= (double) data.length;
		result -= ( xbar*xbar );
		
		
		return result;
	}
	
	/***
	 * Calculate the mean of an array of doubles
	 * 
	 * @param data
	 * @return
	 */
	static double mean(double [] data)
	{
		double result = 0;
		
		for( int i = 0; i < data.length; i++ )
			result += (double) data[i];
		
		return result / data.length;
	}

	/***
	 * Calculate the variance of an array of longs
	 * Use the formula Var = E[X^2] - E[X]^2
	 * 
	 * Agrees with LibreOffice Calc for N=1000
	 * 
	 * @param data
	 * @return
	 */
	static double variance(long [] data)
	{
		double result = 0;
		double xbar = mean(data);
		
		for( int i = 0; i < data.length; i++ )
		{
			result += ( (double) data[i]*data[i] );
		}
		
		result /= (double) data.length;
		result -= (xbar*xbar);
		
		return result;
	}
	
	/***
	 * Calculate the mean of an array of longs
	 * 
	 * @param data
	 * @return mean value for given array 
	 */
	static double mean(long [] data)
	{
		double result = 0;
		
		for( int i = 0; i < data.length; i++ )
			result += (double) data[i];
		
		return result / data.length;
	}
	
	static double trimmedMean(long [] data)
	{
		Arrays.sort(data);
		
		double result = 0;
		int trimq = (int) ((int) data.length * 0.1); // trim 10% of the data
		
		
		for( int i = trimq/2; i < data.length-trimq/2; i++ )
			result += (double) data[i];
		
		return result / (data.length-trimq);
	}
	
	
	static int IndexOfMin(double[] data) {
		// data.length must be at least 1
		if(data.length < 1) return -1;
		
		double min = data[0];
		int index = 0;
		
		for (int i = 1; i < data.length; i++) {
			if(min > data[i]){ 
				min = data[i];
				index = i;
			}
		}
		
		return index;
	}
	
	static int IndexOfMax(double[] data) {
		// data.length must be at least 1
		if(data.length < 1) return -1;
		
		double max = data[0];
		int index = 0;
		
		for (int i = 1; i < data.length; i++) {
			if(max < data[i]){ 
				max = data[i];
				index = i;
			}
		}
		
		return index;
	}
	
	public static double median(double[] m) {
		Arrays.sort(m);
		
	    int middle = m.length/2;  // subscript of middle element
	    if (m.length%2 == 1) {
	        // Odd number of elements -- return the middle one.
	        return m[middle];
	    } else {
	       // Even number -- return average of middle two
	       // Must cast the numbers to double before dividing.
	       return (m[middle-1] + m[middle]) / 2.0;
	    }
	}//end method median
	
	public static double median(long[] m) {
		Arrays.sort(m);
		
	    int middle = m.length/2;  // subscript of middle element
	    if (m.length%2 == 1) {
	        // Odd number of elements -- return the middle one.
	        return m[middle];
	    } else {
	       // Even number -- return average of middle two
	       // Must cast the numbers to double before dividing.
	       return (m[middle-1] + m[middle]) / 2.0;
	    }
	}//end method median
	
	
	public static void main(String[] args) {
		
		long[] data = new long[1000];
		trimmedMean(data);
		
	}

	public static String toHex(byte[] digest) {
		String hexString = "";
		
		for (int i = 0; i < digest.length-1; i++) {
				hexString += toHex(digest[i]) + ":";
		}	
		hexString += toHex(digest[digest.length-1]);
		
		return hexString;
		
				
	}
	
	public static String toHex(byte digest) {
		
		if(digest>=0 && digest<=15)
			return String.format("0%x",digest);
		else
			return String.format("%x",digest);
		
	}



}
